package com.xshang.tenant;

import com.xshang.entity.TenantDs;
import com.xshang.service.TenantService;
import com.xshang.util.JsonUtil;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 租户数据源配置服务
 */
@Service
@Slf4j
public class TenantDsServiceImpl extends DefaultTenantDataSourceService implements ApplicationListener<ApplicationStartedEvent> {
    /**
     * 缓存已经初始化的租户数据源名称集合
     */
    private static final Map<String, String> INITIALED_TENANT_DATA_SOURCES = new HashMap<>();

    /**
     * 查询租户信息列表对象
     */
    @Resource
    private TenantService tenantService;
    
    /**
     * 感知应用启动事件：应用启动后，首次初始化租户数据源
     *
     * @param event 应用启动事件
     */
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        log.info("应用程序已启动:{}, 初始租户数据源开始", event.getClass().getTypeName());
        int count = initTenantDataSources();
        log.info("初始化数据源: {}", count);
        log.info("初始租户数据源结束.");
    }

    @Override
    public void refresh() {
        // 1、加载新租户的数据源
        initNewTenantDataSources();
        // todo 2、刷新有变更的租户数据源：先删除然后重新初始化
        // todo 3、删除过期租户或无效的数据源
        log.info("租户数据源已刷新.");
    }

    private int initTenantDataSources() {
        List<TenantDs> tenantDataSources = queryAllTenantDataSources();
        if (CollectionUtils.isEmpty(tenantDataSources)) {
            return 0;
        }
        return buildTenantDataSources(tenantDataSources);
    }

    private void initNewTenantDataSources() {
        List<TenantDs> newTenantDataSources = getNewTenantDataSources();
        if (CollectionUtils.isEmpty(newTenantDataSources)) {
            return;
        }
        List<String> newTenantDataSourceNames =
                newTenantDataSources.stream().map(TenantDs::getDsName).collect(Collectors.toList());
        log.info("监听到到新租户数据源，正在初始化: {}", JsonUtil.objToJson(newTenantDataSourceNames));
        int count = buildTenantDataSources(newTenantDataSources);
        log.info("{} 新租户 DataSource 初始化完成: {}.", count, JsonUtil.objToJson(newTenantDataSourceNames));
    }

    private List<TenantDs> getNewTenantDataSources() {
        List<TenantDs> tenantDataSources = queryAllTenantDataSources();
        if (CollectionUtils.isEmpty(tenantDataSources)) {
            return new ArrayList<>();
        }
        return tenantDataSources.stream()
                .filter(tenantDataSource -> !INITIALED_TENANT_DATA_SOURCES.containsKey(String.valueOf(tenantDataSource.getTenantId())))
                .collect(Collectors.toList());
    }

    private int buildTenantDataSources(List<TenantDs> tenantDataSources) {
        // 构建新的租户数据源
        for (TenantDs tenantDataSource : tenantDataSources) {
            DataSourceProperties prop = getDataSourceProperties(tenantDataSource);
            System.out.println(prop.getUrl());
            HikariDataSource dataSource = TenantDataSourceBuilder.build(prop);
            add(tenantDataSource.getDsName(), dataSource);
        }
        // 缓存已初始化的租户数据源名称集合
        INITIALED_TENANT_DATA_SOURCES.putAll(tenantDataSources.stream()
                .collect(Collectors.toMap(
                        tenantDs -> String.valueOf(tenantDs.getTenantId()),
                        TenantDs::getDsName)));
        return INITIALED_TENANT_DATA_SOURCES.size();
    }

    private static DataSourceProperties getDataSourceProperties(TenantDs tenantDataSource) {
        DataSourceProperties prop = new DataSourceProperties();
        prop.setDriverClassName("com.mysql.cj.jdbc.Driver");
        prop.setUrl("jdbc:mysql://localhost:3306/" + tenantDataSource.getDsSchema() + "?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull");
        prop.setUsername(tenantDataSource.getDsUsername());
        prop.setPassword(tenantDataSource.getDsPassword());
        return prop;
    }

    /**
     * 实现租户到数据源名称（租户数据源bean的名称）：如果缓存中没有，则返回缺省数据源
     * 
     * @param tenantId 租户id
     * @return 租户数据源bean的名称
     */
    @Override
    public String getTenantDataSourceBeanName(String tenantId) {
        if (INITIALED_TENANT_DATA_SOURCES.containsKey(tenantId)) {
            return INITIALED_TENANT_DATA_SOURCES.get(tenantId);
        }
        log.warn("{} 未找到租户数据源，请检查! [将返回默认数据源]", tenantId);
        // 返回缺省数据源或抛出异常：也可以尝试一次数据库查询，即时初始化新增的租户数据源，如果存在的话
        return TenantDataSourceContext.DEFAULT_TENANT_DATA_SOURCE;
    }

    /**
     * 从主数据库或缺省数据库中查询所有租户信息列表
     * 
     * @return 所有租户信息列表
     */
    private List<TenantDs> queryAllTenantDataSources() {
        List<TenantDs> tenantDsList = tenantService.findAll();
        if (CollectionUtils.isEmpty(tenantDsList)) {
            return new ArrayList<>();
        }
        return tenantDsList;
    }
}
